This R code downloads language data by census tract for Travis County, Texas, using 2013-2017 5-year ACS estimates. Then it maps those languages alongside Austin Public Health facilities. The goal is to reveal any areas that might be underserved.

Helpful websites:

library(tidyverse)
library(tidycensus)
library(viridis)
library(sf)
options(tigris_use_cache = TRUE)

First read in Austin Public Health Locations data. This is found on the city open data portal (link?) but first you must break out lat and lon fields. Currently the dataset has lat and lon as part of the address field. Also added one more location, and then saved as an Excel file for the import.

Austin_Public_Health_Locations <- read_excel("Austin_Public_Health_Locations.xlsx")

Use tidycensus package to connect via Census API to download numbers of language speakers in each census tract. Include a variable for summary_var (total number of people in the census tract) and calculate percentages. Those percentage values will be used as the highlighted attribute for each census tract.

spanish <- get_acs(geography = "tract", variables = "C16001_003", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
chinese <- get_acs(geography = "tract", variables = "C16001_021", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
vietnamese <- get_acs(geography = "tract", variables = "C16001_024", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
otherasian <- get_acs(geography = "tract", variables = "C16001_030", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
frenchhaitian <- get_acs(geography = "tract", variables = "C16001_006", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
german <- get_acs(geography = "tract", variables = "C16001_009", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
russian <- get_acs(geography = "tract", variables = "C16001_012", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
otherindoeuropean <- get_acs(geography = "tract", variables = "C16001_015", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
korean <- get_acs(geography = "tract", variables = "C16001_018", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
tagalog <- get_acs(geography = "tract", variables = "C16001_027", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
arabic <- get_acs(geography = "tract", variables = "C16001_033", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))
other <- get_acs(geography = "tract", variables = "C16001_036", year = 2017, state = 48, county = c("Travis"), summary_var = "C16001_001", geometry = TRUE, survey = "acs5") %>% mutate(pct = round(100 * (estimate / summary_est), digits = 1))

This builds a new field in the data file, converting lat and lon fields into simple feature polygon geometry.

aph <- st_as_sf(Austin_Public_Health_Locations, coords = c("lon", "lat"),  crs = st_crs(spanish))

Produce a table of all languages and total # speakers.

totals <- tribble(~Lang, ~Total, "Spanish", sum(spanish$estimate), "Chinese", sum(chinese$estimate))
totals <- add_row(totals, Lang = "Vietnamese", Total = sum(vietnamese$estimate))
totals <- add_row(totals, Lang = "German", Total = sum(german$estimate))
totals <- add_row(totals, Lang = "French Haitian or Cajun", Total = sum(frenchhaitian$estimate))
totals <- add_row(totals, Lang = "Russian Polish Other Slavic", Total = sum(russian$estimate))
totals <- add_row(totals, Lang = "Other Indo-European", Total = sum(otherindoeuropean$estimate))
totals <- add_row(totals, Lang = "Korean", Total = sum(korean$estimate))
totals <- add_row(totals, Lang = "Tagalog", Total = sum(tagalog$estimate))
totals <- add_row(totals, Lang = "Arabic", Total = sum(arabic$estimate))
totals <- add_row(totals, Lang = "Other", Total = sum(other$estimate))
totals <- add_row(totals, Lang = "Other Asian", Total = sum(otherasian$estimate))
# Sort by # of speakers and print the totals.
totals <- totals %>% arrange(desc(Total))
totals

Remove tracts from the map with zero speakers. Need to expand this part. For now, just use a filter.

vietnamese <- filter(vietnamese, pct > 0)

Set the color brewer ramp colors. We pick the six monochromatic ramps in RColorBrewer.

library(RColorBrewer)
reds.pal <- colorRampPalette(brewer.pal(9, "Reds"))
purples.pal <- colorRampPalette(brewer.pal(9, "Purples"))
oranges.pal <- colorRampPalette(brewer.pal(9, "Oranges"))
greys.pal <- colorRampPalette(brewer.pal(9, "Greys"))
greens.pal <- colorRampPalette(brewer.pal(9, "Greens"))
blues.pal <- colorRampPalette(brewer.pal(9, "Blues"))

Build the first map, with the top five languages in the city. Map is shown below; larger version on a standalone html page can be found here.

map <- mapview(aph, zcol = "Facility Name", legend = FALSE, col.regions = "yellow", homebutton = FALSE)
map <- map + mapview(spanish, zcol = "pct", col.regions=reds.pal, homebutton = FALSE)
map <- map + mapview(otherindoeuropean, zcol = "pct", col.regions=purples.pal, homebutton = FALSE)
map <- map + mapview(chinese, zcol = "pct", col.regions=oranges.pal, homebutton = FALSE)
map <- map + mapview(vietnamese, zcol = "pct", col.regions=greens.pal, homebutton = FALSE)
map <- map + mapview(otherasian, zcol = "pct", col.regions=blues.pal, homebutton = FALSE)
mapshot(map, url = paste0(getwd(), "/map1.html"))
map

Build the second map, of the bottom seven languages. Map below; final map can also be found here.

map <- mapview(aph, zcol = "Facility Name", legend = FALSE, col.regions = "yellow", homebutton = FALSE)
map <- map + mapview(frenchhaitian, zcol = "pct", col.regions=reds.pal, homebutton = FALSE)
map <- map + mapview(korean, zcol = "pct", col.regions=purples.pal, homebutton = FALSE)
map <- map + mapview(arabic, zcol = "pct", col.regions=oranges.pal, homebutton = FALSE)
map <- map + mapview(other, zcol = "pct", col.regions=greens.pal, homebutton = FALSE)
map <- map + mapview(russian, zcol = "pct", col.regions=blues.pal, homebutton = FALSE)
map <- map + mapview(german, zcol = "pct", col.regions=reds.pal, homebutton = FALSE)
map <- map + mapview(tagalog, zcol = "pct", col.regions=purples.pal, homebutton = FALSE)
mapshot(map, url = paste0(getwd(), "/map2.html"))
map
LS0tCnRpdGxlOiAiTGFuZ3VhZ2UgTWFwcyBmb3IgQXVzdGluIFB1YmxpYyBIZWFsdGgiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KVGhpcyBSIGNvZGUgZG93bmxvYWRzIGxhbmd1YWdlIGRhdGEgYnkgY2Vuc3VzIHRyYWN0IGZvciBUcmF2aXMgQ291bnR5LCBUZXhhcywgdXNpbmcgMjAxMy0yMDE3IDUteWVhciBBQ1MgZXN0aW1hdGVzLiBUaGVuIGl0IG1hcHMgdGhvc2UgbGFuZ3VhZ2VzIGFsb25nc2lkZSBBdXN0aW4gUHVibGljIEhlYWx0aCBmYWNpbGl0aWVzLiBUaGUgZ29hbCBpcyB0byByZXZlYWwgYW55IGFyZWFzIHRoYXQgbWlnaHQgYmUgdW5kZXJzZXJ2ZWQuCgpIZWxwZnVsIHdlYnNpdGVzOgoKKiBodHRwczovL3dhbGtlcmtlLmdpdGh1Yi5pby90aWR5Y2Vuc3VzL2FydGljbGVzL2Jhc2ljLXVzYWdlLmh0bWwKKiBodHRwczovL3dhbGtlcmtlLmdpdGh1Yi5pby90aWR5Y2Vuc3VzL2FydGljbGVzL3NwYXRpYWwtZGF0YS5odG1sCiogaHR0cHM6Ly9tYXAtcmZ1bi5saWJyYXJ5LmR1a2UuZWR1LzAyX2Nob3JvcGxldGguaHRtbAoKCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeWNlbnN1cykKbGlicmFyeSh2aXJpZGlzKQpsaWJyYXJ5KHNmKQpvcHRpb25zKHRpZ3Jpc191c2VfY2FjaGUgPSBUUlVFKQpgYGAKRmlyc3QgcmVhZCBpbiBBdXN0aW4gUHVibGljIEhlYWx0aCBMb2NhdGlvbnMgZGF0YS4gVGhpcyBpcyBmb3VuZCBvbiB0aGUgY2l0eSBvcGVuIGRhdGEgcG9ydGFsIChsaW5rPykgYnV0IGZpcnN0IHlvdSBtdXN0IGJyZWFrIG91dCBsYXQgYW5kIGxvbiBmaWVsZHMuIEN1cnJlbnRseSB0aGUgZGF0YXNldCBoYXMgbGF0IGFuZCBsb24gYXMgcGFydCBvZiB0aGUgYWRkcmVzcyBmaWVsZC4gQWxzbyBhZGRlZCBvbmUgbW9yZSBsb2NhdGlvbiwgYW5kIHRoZW4gc2F2ZWQgYXMgYW4gRXhjZWwgZmlsZSBmb3IgdGhlIGltcG9ydC4KCmBgYHtyfQpBdXN0aW5fUHVibGljX0hlYWx0aF9Mb2NhdGlvbnMgPC0gcmVhZF9leGNlbCgiQXVzdGluX1B1YmxpY19IZWFsdGhfTG9jYXRpb25zLnhsc3giKQpgYGAKClVzZSB0aWR5Y2Vuc3VzIHBhY2thZ2UgdG8gY29ubmVjdCB2aWEgQ2Vuc3VzIEFQSSB0byBkb3dubG9hZCBudW1iZXJzIG9mIGxhbmd1YWdlIHNwZWFrZXJzIGluIGVhY2ggY2Vuc3VzIHRyYWN0LiBJbmNsdWRlIGEgdmFyaWFibGUgZm9yIHN1bW1hcnlfdmFyICh0b3RhbCBudW1iZXIgb2YgcGVvcGxlIGluIHRoZSBjZW5zdXMgdHJhY3QpIGFuZCBjYWxjdWxhdGUgcGVyY2VudGFnZXMuIFRob3NlIHBlcmNlbnRhZ2UgdmFsdWVzIHdpbGwgYmUgdXNlZCBhcyB0aGUgaGlnaGxpZ2h0ZWQgYXR0cmlidXRlIGZvciBlYWNoIGNlbnN1cyB0cmFjdC4KCmBgYHtyfQpzcGFuaXNoIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMDMiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQpjaGluZXNlIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMjEiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQp2aWV0bmFtZXNlIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMjQiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQpvdGhlcmFzaWFuIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMzAiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQpmcmVuY2hoYWl0aWFuIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMDYiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQpnZXJtYW4gPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCB2YXJpYWJsZXMgPSAiQzE2MDAxXzAwOSIsIHllYXIgPSAyMDE3LCBzdGF0ZSA9IDQ4LCBjb3VudHkgPSBjKCJUcmF2aXMiKSwgc3VtbWFyeV92YXIgPSAiQzE2MDAxXzAwMSIsIGdlb21ldHJ5ID0gVFJVRSwgc3VydmV5ID0gImFjczUiKSAlPiUgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIChlc3RpbWF0ZSAvIHN1bW1hcnlfZXN0KSwgZGlnaXRzID0gMSkpCnJ1c3NpYW4gPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCB2YXJpYWJsZXMgPSAiQzE2MDAxXzAxMiIsIHllYXIgPSAyMDE3LCBzdGF0ZSA9IDQ4LCBjb3VudHkgPSBjKCJUcmF2aXMiKSwgc3VtbWFyeV92YXIgPSAiQzE2MDAxXzAwMSIsIGdlb21ldHJ5ID0gVFJVRSwgc3VydmV5ID0gImFjczUiKSAlPiUgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIChlc3RpbWF0ZSAvIHN1bW1hcnlfZXN0KSwgZGlnaXRzID0gMSkpCm90aGVyaW5kb2V1cm9wZWFuIDwtIGdldF9hY3MoZ2VvZ3JhcGh5ID0gInRyYWN0IiwgdmFyaWFibGVzID0gIkMxNjAwMV8wMTUiLCB5ZWFyID0gMjAxNywgc3RhdGUgPSA0OCwgY291bnR5ID0gYygiVHJhdmlzIiksIHN1bW1hcnlfdmFyID0gIkMxNjAwMV8wMDEiLCBnZW9tZXRyeSA9IFRSVUUsIHN1cnZleSA9ICJhY3M1IikgJT4lIG11dGF0ZShwY3QgPSByb3VuZCgxMDAgKiAoZXN0aW1hdGUgLyBzdW1tYXJ5X2VzdCksIGRpZ2l0cyA9IDEpKQprb3JlYW4gPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCB2YXJpYWJsZXMgPSAiQzE2MDAxXzAxOCIsIHllYXIgPSAyMDE3LCBzdGF0ZSA9IDQ4LCBjb3VudHkgPSBjKCJUcmF2aXMiKSwgc3VtbWFyeV92YXIgPSAiQzE2MDAxXzAwMSIsIGdlb21ldHJ5ID0gVFJVRSwgc3VydmV5ID0gImFjczUiKSAlPiUgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIChlc3RpbWF0ZSAvIHN1bW1hcnlfZXN0KSwgZGlnaXRzID0gMSkpCnRhZ2Fsb2cgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCB2YXJpYWJsZXMgPSAiQzE2MDAxXzAyNyIsIHllYXIgPSAyMDE3LCBzdGF0ZSA9IDQ4LCBjb3VudHkgPSBjKCJUcmF2aXMiKSwgc3VtbWFyeV92YXIgPSAiQzE2MDAxXzAwMSIsIGdlb21ldHJ5ID0gVFJVRSwgc3VydmV5ID0gImFjczUiKSAlPiUgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIChlc3RpbWF0ZSAvIHN1bW1hcnlfZXN0KSwgZGlnaXRzID0gMSkpCmFyYWJpYyA8LSBnZXRfYWNzKGdlb2dyYXBoeSA9ICJ0cmFjdCIsIHZhcmlhYmxlcyA9ICJDMTYwMDFfMDMzIiwgeWVhciA9IDIwMTcsIHN0YXRlID0gNDgsIGNvdW50eSA9IGMoIlRyYXZpcyIpLCBzdW1tYXJ5X3ZhciA9ICJDMTYwMDFfMDAxIiwgZ2VvbWV0cnkgPSBUUlVFLCBzdXJ2ZXkgPSAiYWNzNSIpICU+JSBtdXRhdGUocGN0ID0gcm91bmQoMTAwICogKGVzdGltYXRlIC8gc3VtbWFyeV9lc3QpLCBkaWdpdHMgPSAxKSkKb3RoZXIgPC0gZ2V0X2FjcyhnZW9ncmFwaHkgPSAidHJhY3QiLCB2YXJpYWJsZXMgPSAiQzE2MDAxXzAzNiIsIHllYXIgPSAyMDE3LCBzdGF0ZSA9IDQ4LCBjb3VudHkgPSBjKCJUcmF2aXMiKSwgc3VtbWFyeV92YXIgPSAiQzE2MDAxXzAwMSIsIGdlb21ldHJ5ID0gVFJVRSwgc3VydmV5ID0gImFjczUiKSAlPiUgbXV0YXRlKHBjdCA9IHJvdW5kKDEwMCAqIChlc3RpbWF0ZSAvIHN1bW1hcnlfZXN0KSwgZGlnaXRzID0gMSkpCmBgYAoKVGhpcyBidWlsZHMgYSBuZXcgZmllbGQgaW4gdGhlIGRhdGEgZmlsZSwgY29udmVydGluZyBsYXQgYW5kIGxvbiBmaWVsZHMgaW50byBzaW1wbGUgZmVhdHVyZSBwb2x5Z29uIGdlb21ldHJ5LgoKYGBge3J9CmFwaCA8LSBzdF9hc19zZihBdXN0aW5fUHVibGljX0hlYWx0aF9Mb2NhdGlvbnMsIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwgIGNycyA9IHN0X2NycyhzcGFuaXNoKSkKYGBgCgpQcm9kdWNlIGEgdGFibGUgb2YgYWxsIGxhbmd1YWdlcyBhbmQgdG90YWwgIyBzcGVha2Vycy4KCmBgYHtyfQp0b3RhbHMgPC0gdHJpYmJsZSh+TGFuZywgflRvdGFsLCAiU3BhbmlzaCIsIHN1bShzcGFuaXNoJGVzdGltYXRlKSwgIkNoaW5lc2UiLCBzdW0oY2hpbmVzZSRlc3RpbWF0ZSkpCnRvdGFscyA8LSBhZGRfcm93KHRvdGFscywgTGFuZyA9ICJWaWV0bmFtZXNlIiwgVG90YWwgPSBzdW0odmlldG5hbWVzZSRlc3RpbWF0ZSkpCnRvdGFscyA8LSBhZGRfcm93KHRvdGFscywgTGFuZyA9ICJHZXJtYW4iLCBUb3RhbCA9IHN1bShnZXJtYW4kZXN0aW1hdGUpKQp0b3RhbHMgPC0gYWRkX3Jvdyh0b3RhbHMsIExhbmcgPSAiRnJlbmNoIEhhaXRpYW4gb3IgQ2FqdW4iLCBUb3RhbCA9IHN1bShmcmVuY2hoYWl0aWFuJGVzdGltYXRlKSkKdG90YWxzIDwtIGFkZF9yb3codG90YWxzLCBMYW5nID0gIlJ1c3NpYW4gUG9saXNoIE90aGVyIFNsYXZpYyIsIFRvdGFsID0gc3VtKHJ1c3NpYW4kZXN0aW1hdGUpKQp0b3RhbHMgPC0gYWRkX3Jvdyh0b3RhbHMsIExhbmcgPSAiT3RoZXIgSW5kby1FdXJvcGVhbiIsIFRvdGFsID0gc3VtKG90aGVyaW5kb2V1cm9wZWFuJGVzdGltYXRlKSkKdG90YWxzIDwtIGFkZF9yb3codG90YWxzLCBMYW5nID0gIktvcmVhbiIsIFRvdGFsID0gc3VtKGtvcmVhbiRlc3RpbWF0ZSkpCnRvdGFscyA8LSBhZGRfcm93KHRvdGFscywgTGFuZyA9ICJUYWdhbG9nIiwgVG90YWwgPSBzdW0odGFnYWxvZyRlc3RpbWF0ZSkpCnRvdGFscyA8LSBhZGRfcm93KHRvdGFscywgTGFuZyA9ICJBcmFiaWMiLCBUb3RhbCA9IHN1bShhcmFiaWMkZXN0aW1hdGUpKQp0b3RhbHMgPC0gYWRkX3Jvdyh0b3RhbHMsIExhbmcgPSAiT3RoZXIiLCBUb3RhbCA9IHN1bShvdGhlciRlc3RpbWF0ZSkpCnRvdGFscyA8LSBhZGRfcm93KHRvdGFscywgTGFuZyA9ICJPdGhlciBBc2lhbiIsIFRvdGFsID0gc3VtKG90aGVyYXNpYW4kZXN0aW1hdGUpKQoKIyBTb3J0IGJ5ICMgb2Ygc3BlYWtlcnMgYW5kIHByaW50IHRoZSB0b3RhbHMuCnRvdGFscyA8LSB0b3RhbHMgJT4lIGFycmFuZ2UoZGVzYyhUb3RhbCkpCnRvdGFscwpgYGAKClJlbW92ZSB0cmFjdHMgZnJvbSB0aGUgbWFwIHdpdGggemVybyBzcGVha2Vycy4gTmVlZCB0byBleHBhbmQgdGhpcyBwYXJ0LiBGb3Igbm93LCBqdXN0IHVzZSBhIGZpbHRlci4KCmBgYHtyfQp2aWV0bmFtZXNlIDwtIGZpbHRlcih2aWV0bmFtZXNlLCBwY3QgPiAwKQpgYGAKClNldCB0aGUgY29sb3IgYnJld2VyIHJhbXAgY29sb3JzLiBXZSBwaWNrIHRoZSBzaXggbW9ub2Nocm9tYXRpYyByYW1wcyBpbiBSQ29sb3JCcmV3ZXIuCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCnJlZHMucGFsIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg5LCAiUmVkcyIpKQpwdXJwbGVzLnBhbCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOSwgIlB1cnBsZXMiKSkKb3Jhbmdlcy5wYWwgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDksICJPcmFuZ2VzIikpCmdyZXlzLnBhbCA8LSBjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOSwgIkdyZXlzIikpCmdyZWVucy5wYWwgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDksICJHcmVlbnMiKSkKYmx1ZXMucGFsIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg5LCAiQmx1ZXMiKSkKYGBgCgpCdWlsZCB0aGUgZmlyc3QgbWFwLCB3aXRoIHRoZSB0b3AgZml2ZSBsYW5ndWFnZXMgaW4gdGhlIGNpdHkuIE1hcCBpcyBzaG93biBiZWxvdzsgbGFyZ2VyIHZlcnNpb24gb24gYSBzdGFuZGFsb25lIGh0bWwgcGFnZSBjYW4gYmUgZm91bmQgW2hlcmVdKG1hcDEuaHRtbCkuCgpgYGB7cn0KbWFwIDwtIG1hcHZpZXcoYXBoLCB6Y29sID0gIkZhY2lsaXR5IE5hbWUiLCBsZWdlbmQgPSBGQUxTRSwgY29sLnJlZ2lvbnMgPSAieWVsbG93IiwgaG9tZWJ1dHRvbiA9IEZBTFNFKQptYXAgPC0gbWFwICsgbWFwdmlldyhzcGFuaXNoLCB6Y29sID0gInBjdCIsIGNvbC5yZWdpb25zPXJlZHMucGFsLCBob21lYnV0dG9uID0gRkFMU0UpCm1hcCA8LSBtYXAgKyBtYXB2aWV3KG90aGVyaW5kb2V1cm9wZWFuLCB6Y29sID0gInBjdCIsIGNvbC5yZWdpb25zPXB1cnBsZXMucGFsLCBob21lYnV0dG9uID0gRkFMU0UpCm1hcCA8LSBtYXAgKyBtYXB2aWV3KGNoaW5lc2UsIHpjb2wgPSAicGN0IiwgY29sLnJlZ2lvbnM9b3Jhbmdlcy5wYWwsIGhvbWVidXR0b24gPSBGQUxTRSkKbWFwIDwtIG1hcCArIG1hcHZpZXcodmlldG5hbWVzZSwgemNvbCA9ICJwY3QiLCBjb2wucmVnaW9ucz1ncmVlbnMucGFsLCBob21lYnV0dG9uID0gRkFMU0UpCm1hcCA8LSBtYXAgKyBtYXB2aWV3KG90aGVyYXNpYW4sIHpjb2wgPSAicGN0IiwgY29sLnJlZ2lvbnM9Ymx1ZXMucGFsLCBob21lYnV0dG9uID0gRkFMU0UpCgptYXBzaG90KG1hcCwgdXJsID0gcGFzdGUwKGdldHdkKCksICIvbWFwMS5odG1sIikpCm1hcApgYGAKCkJ1aWxkIHRoZSBzZWNvbmQgbWFwLCBvZiB0aGUgYm90dG9tIHNldmVuIGxhbmd1YWdlcy4gTWFwIGJlbG93OyBmaW5hbCBtYXAgY2FuIGFsc28gYmUgZm91bmQgW2hlcmVdKG1hcDIuaHRtbCkuCgpgYGB7cn0KbWFwIDwtIG1hcHZpZXcoYXBoLCB6Y29sID0gIkZhY2lsaXR5IE5hbWUiLCBsZWdlbmQgPSBGQUxTRSwgY29sLnJlZ2lvbnMgPSAieWVsbG93IiwgaG9tZWJ1dHRvbiA9IEZBTFNFKQptYXAgPC0gbWFwICsgbWFwdmlldyhmcmVuY2hoYWl0aWFuLCB6Y29sID0gInBjdCIsIGNvbC5yZWdpb25zPXJlZHMucGFsLCBob21lYnV0dG9uID0gRkFMU0UpCm1hcCA8LSBtYXAgKyBtYXB2aWV3KGtvcmVhbiwgemNvbCA9ICJwY3QiLCBjb2wucmVnaW9ucz1wdXJwbGVzLnBhbCwgaG9tZWJ1dHRvbiA9IEZBTFNFKQptYXAgPC0gbWFwICsgbWFwdmlldyhhcmFiaWMsIHpjb2wgPSAicGN0IiwgY29sLnJlZ2lvbnM9b3Jhbmdlcy5wYWwsIGhvbWVidXR0b24gPSBGQUxTRSkKbWFwIDwtIG1hcCArIG1hcHZpZXcob3RoZXIsIHpjb2wgPSAicGN0IiwgY29sLnJlZ2lvbnM9Z3JlZW5zLnBhbCwgaG9tZWJ1dHRvbiA9IEZBTFNFKQptYXAgPC0gbWFwICsgbWFwdmlldyhydXNzaWFuLCB6Y29sID0gInBjdCIsIGNvbC5yZWdpb25zPWJsdWVzLnBhbCwgaG9tZWJ1dHRvbiA9IEZBTFNFKQptYXAgPC0gbWFwICsgbWFwdmlldyhnZXJtYW4sIHpjb2wgPSAicGN0IiwgY29sLnJlZ2lvbnM9cmVkcy5wYWwsIGhvbWVidXR0b24gPSBGQUxTRSkKbWFwIDwtIG1hcCArIG1hcHZpZXcodGFnYWxvZywgemNvbCA9ICJwY3QiLCBjb2wucmVnaW9ucz1wdXJwbGVzLnBhbCwgaG9tZWJ1dHRvbiA9IEZBTFNFKQoKbWFwc2hvdChtYXAsIHVybCA9IHBhc3RlMChnZXR3ZCgpLCAiL21hcDIuaHRtbCIpKQptYXAKYGBgCgo=